#include <iostream>
#include <sstream>
#include <uv.h>

#define _UVERR(call, err) std::cerr << call << " failed because " << uv_strerror(err) << std::endl
#define UVERR(call, err) { if (err) { _UVERR(call, err); } }
#define UVERRRET(call, err) { if (err) { _UVERR(call, err); return; } }
#define UVERRRETVAL(call, err) { if (err) { _UVERR(call, err); return err; } }
#define UVCALL(call, ...) { if (auto err = call(__VA_ARGS__)) { UVERR(#call, err); } }
#define UVCALLRET(call, ...) { if (auto err = call(__VA_ARGS__)) { UVERRRET(#call, err); } }
#define UVCALLRETVAL(call, ...) { if (auto err = call(__VA_ARGS__)) { UVERRRETVAL(#call, err); } }

class Main {
    struct WrittenBuffer {
        uv_write_t req;
        uv_buf_t buf;
    };

    union {
        struct sockaddr base;
        struct sockaddr_in addr;
    } addr;
    uv_loop_t *loop = uv_default_loop();
    uv_tcp_t server = {.data = this};
    uv_connect_t connect = {.data = this};
    uv_timer_t timer = {.data = this};
    uv_signal_t signal = {.data = this};
    int32_t count = 0;

    template<typename T>
    static Main &GetInstance(T *that)
    {
        return *reinterpret_cast<Main *>(that->data);
    }

    static void OnConnectted(uv_connect_t *connect, int err)
    {
        UVERR(__func__, err);
        auto &that = GetInstance(connect);
        UVCALL(uv_timer_start, &that.timer, OnTimer, 0, 1000);
    }

    static void OnTimer(uv_timer_t *timer)
    {
        auto &that = GetInstance(timer);
        printf("count: %d\n", that.count);
        std::stringstream ss;
        ss << that.count++;
        auto content = ss.str();

        auto wbuffer = new WrittenBuffer();
        wbuffer->buf.base = new char[content.size() + 1];
        wbuffer->buf.len = content.size() + 1;
        for (size_t i = 0; i <= content.size(); i++) {
            wbuffer->buf.base[i] = content.c_str()[i];
        }

        UVCALL(uv_write, &wbuffer->req, that.connect.handle, &wbuffer->buf, 1, Written);
    }

    static void Written(uv_write_t *req, int err)
    {
        UVERR(__func__, err);

        auto wbuffer = reinterpret_cast<WrittenBuffer *>(
            reinterpret_cast<char *>(req) - offsetof(WrittenBuffer, req));
        delete[] wbuffer->buf.base;
        delete wbuffer;
    }

    static void OnSignal(uv_signal_t *signal, int32_t signum)
    {
        if (signum == SIGINT) {
            auto &that = GetInstance(signal);
            UVCALL(uv_signal_stop, signal);
            uv_stop(that.loop);
        }
    }

public:
    int Run(const char *address, const int port)
    {
        UVCALLRETVAL(uv_tcp_init, loop, &server);
        UVCALLRETVAL(uv_ip4_addr, address, port, &addr.addr);
        UVCALLRETVAL(uv_tcp_connect, &connect, &server, &addr.base, OnConnectted);
        UVCALLRETVAL(uv_timer_init, loop, &timer);
        UVCALLRETVAL(uv_signal_init, loop, &signal);
        UVCALLRETVAL(uv_signal_start, &signal, OnSignal, SIGINT);
        UVCALLRETVAL(uv_run, loop, UV_RUN_DEFAULT);
        return 0;
    }
};

int main()
{
    Main main;
    return main.Run("127.0.0.1", 7000);
}
